true

Intro

In this section we will be working with shapefiles. More specifically how to read in a shapefile and join this to our aggregated crime count data frame. From there we introduce classification methods as a way to better visualize crime counts.

Load packages

As always the first step is to load the necessary R packages via the library function. If you do not have these packages installed then please follow the instructions in the Preliminary Task.Rmd file.


# for data reading/manipulation 
library(dplyr)
library(tidyr)
library(readr)
library(tibble)
library(janitor)
# for spatial data and gis
library(sf)
library(ggmap)
library(ggplot2)
library(ggspatial)
library(spdep)
library(leaflet) 
library(RColorBrewer)
library(tmap)

What is a Shapefile?

They represent a geospatial vector that is used for GIS software. Shapefiles store both geogrpahic location and its associated attribute information

The Shapefile format stores the data as primitive geometric shapes like points, lines, and polygons. These shapes, together with data attributes that are linked to each shape, create the representation of the geographic data.

They contain four mandatory file extensions (.shx, .shp, .dbf and the .prj). - The .shp contains the geometry data (a 2D axis ordering of coordinate data) - The .shx contains the positional index of the feature geometry - The .dbf contins the attributes for each shape - The .prj contains the cs and projection information

In criminological research, the LSOA is quite frequently used as the main census geography

Where to obtain shapefiles

I collected my shapefile data via the UKDS Census Support (https://borders.ukdataservice.ac.uk/bds.html). If you want specific information about how to use the website, please refer to the Downloading the data document again

Read in the Shapefile for ‘Surrey Heath’

shp_file <- sf::st_read("/Users/shihaitao/Library/Mobile Documents/com~apple~CloudDocs/Documents/M1_Documents/爱丁堡大学培训/MappingCrimeData/MappingCrimeData/Data/Shapefile/england_lsoa_2021.shp")
Reading layer `england_lsoa_2021' from data source 
  `/Users/shihaitao/Library/Mobile Documents/com~apple~CloudDocs/Documents/M1_Documents/爱丁堡大学培训/MappingCrimeData/MappingCrimeData/Data/Shapefile/england_lsoa_2021.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 55 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 485406.9 ymin: 154122.5 xmax: 501181.2 ymax: 166842.9
Projected CRS: OSGB36 / British National Grid

You can also use the head() function in shapefiles!

head(shp_file)
Simple feature collection with 6 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 487591.7 ymin: 156900.2 xmax: 500937.4 ymax: 166842.9
Projected CRS: OSGB36 / British National Grid
               name                                         label          lsoa21nm  lsoa21cd                       geometry
1 Surrey Heath 001A E92000001E12000008E07000214E02006416E01030763 Surrey Heath 001A E01030763 MULTIPOLYGON (((496838.5 16...
2 Surrey Heath 006B E92000001E12000008E07000214E02006421E01030762 Surrey Heath 006B E01030762 MULTIPOLYGON (((494998.5 16...
3 Surrey Heath 007D E92000001E12000008E07000214E02006422E01030777 Surrey Heath 007D E01030777 MULTIPOLYGON (((490461 1601...
4 Surrey Heath 007C E92000001E12000008E07000214E02006422E01030776 Surrey Heath 007C E01030776 MULTIPOLYGON (((490895.1 16...
5 Surrey Heath 001D E92000001E12000008E07000214E02006416E01030809 Surrey Heath 001D E01030809 MULTIPOLYGON (((493209 1648...
6 Surrey Heath 011C E92000001E12000008E07000214E02006426E01030772 Surrey Heath 011C E01030772 MULTIPOLYGON (((488055.3 15...
#or use 'View(shp_file) to view the full dataset which will open up in a new panel

Further Information - To clarify, this is an ‘empty shapefile’, it simply contains the boundary profile of Surrey Heath and does not contain any further attribute information. However, if it did contain further attribute information such as the crime counts, population statistics, IMD counts, then you would not need to join the data as we do in this workshop, but instead you could layer the shapefile over our simple feature object created in Topic 1. More information on this method is available from the workshop that was held in February - all resources are available via the ‘Feb_2021’ folder from the github link [https://github.com/UKDataServiceOpen/Crime_Data_in_R.git]

Lets plot the empty shapefile for Surrey Heath to see what we’re actually looking at.

## Plot the Shapefile 
ggplot() + 
  geom_sf(data = shp_file)

As you can seem, we have a map that details the borders (i.e. shape) between each LSOA in Surrey.

This workshop instead joins the ‘crime’ datatset (in tibble format) to the above shapefile. Our newly created object ‘shp_file’ is in fact a sf object, which is short for a ‘simple feature object’. You can check this by typing class(shp_file).

class(shp_file)
[1] "sf"         "data.frame"

So in total, the shapefile consits of 5 variables. The first 4 variables indicate information about that specific LSOA, we are given the name, LSOA code and LSOA name. We can ignore the column ‘label’ as this is just another reference point.

The column I want to draw attention to is the ‘geometry column’

attributes(shp_file$geometry)
$n_empty
[1] 0

$crs
Coordinate Reference System:
  User input: OSGB36 / British National Grid 
  wkt:
PROJCRS["OSGB36 / British National Grid",
    BASEGEOGCRS["OSGB36",
        DATUM["Ordnance Survey of Great Britain 1936",
            ELLIPSOID["Airy 1830",6377563.396,299.3249646,
                LENGTHUNIT["metre",1]]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433]],
        ID["EPSG",4277]],
    CONVERSION["British National Grid",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",49,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-2,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996012717,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",400000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",-100000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1]],
    USAGE[
        SCOPE["Engineering survey, topographic mapping."],
        AREA["United Kingdom (UK) - offshore to boundary of UKCS within 49°45'N to 61°N and 9°W to 2°E; onshore Great Britain (England, Wales and Scotland). Isle of Man onshore."],
        BBOX[49.75,-9,61.01,2.01]],
    ID["EPSG",27700]]

$class
[1] "sfc_MULTIPOLYGON" "sfc"             

$precision
[1] 0

$bbox
    xmin     ymin     xmax     ymax 
485406.9 154122.5 501181.2 166842.9 

The geometry column can be split into two key sections; the feature and the geometry - The feature in this case is our polygon level (referenced by the multipolygon) which is in fact a simple feature geometery list- column (sfc) - The geometery are the numbers that follow, and more technically known as a ’simple feature geometry (sfg)

The column in the sf data.frame that contains the geometries is a list, of class sfc. We can retrieve the geometry list-column in this case by using st_geometry.

st_geometry(shp_file)
Geometry set for 55 features 
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 485406.9 ymin: 154122.5 xmax: 501181.2 ymax: 166842.9
Projected CRS: OSGB36 / British National Grid
First 5 geometries:
MULTIPOLYGON (((496838.5 166608.5, 496888 16656...
MULTIPOLYGON (((494998.5 160258.5, 494999.7 160...
MULTIPOLYGON (((490461 160153.2, 490461.6 16015...
MULTIPOLYGON (((490895.1 160221.4, 490913.8 160...
MULTIPOLYGON (((493209 164876, 493221 164874, 4...

Now we have a basic understanding of what a shapefile is and how we can import them into r, the next step is run some data manipulation and create some new dataframes that can work with the format of shapefiles.

Group the crimes per lsoa

The original crime data set contains the individual count of reported crime types across LSOAS, therefore the LSOAs are repeated multiple times. This is because you would expect to see multiple crime counts in one LSOA.

In order to highlight how many crimes have occurred in each LSOA, we can count the crimes per LSOA and obtained grouped statistics.

crimes_grouped_by_lsoa <- crime %>%
  group_by(lsoa_code) %>%
  summarise(count=n())

head(crimes_grouped_by_lsoa)

Merge the shapefile to the crime dataset

In our new object you will see two variables, the LSOA and the count of crime in each one.

We can now join the Shapefile (the geospatial vector) and the crimes_grouped_by_losa (the aggregated data)

To join the crimes per lsoa to the shapefile we can use the left_join function that returns all the rows of the table on the left side of the join and matching rows for the table on the right side of join.

surrey_lsoa <- left_join(shp_file, crimes_grouped_by_lsoa, by = c("lsoa21cd" = "lsoa_code"))

head(surrey_lsoa)
Simple feature collection with 6 features and 5 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 487591.7 ymin: 156900.2 xmax: 500937.4 ymax: 166842.9
Projected CRS: OSGB36 / British National Grid
               name                                         label          lsoa21nm  lsoa21cd count
1 Surrey Heath 001A E92000001E12000008E07000214E02006416E01030763 Surrey Heath 001A E01030763    12
2 Surrey Heath 006B E92000001E12000008E07000214E02006421E01030762 Surrey Heath 006B E01030762     6
3 Surrey Heath 007D E92000001E12000008E07000214E02006422E01030777 Surrey Heath 007D E01030777     3
4 Surrey Heath 007C E92000001E12000008E07000214E02006422E01030776 Surrey Heath 007C E01030776     3
5 Surrey Heath 001D E92000001E12000008E07000214E02006416E01030809 Surrey Heath 001D E01030809     3
6 Surrey Heath 011C E92000001E12000008E07000214E02006426E01030772 Surrey Heath 011C E01030772     2
                        geometry
1 MULTIPOLYGON (((496838.5 16...
2 MULTIPOLYGON (((494998.5 16...
3 MULTIPOLYGON (((490461 1601...
4 MULTIPOLYGON (((490895.1 16...
5 MULTIPOLYGON (((493209 1648...
6 MULTIPOLYGON (((488055.3 15...
st_geometry_type(surrey_lsoa)    #view the geometery type 
 [1] MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON
[10] MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON
[19] MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON
[28] MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON
[37] MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON
[46] MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON MULTIPOLYGON
[55] MULTIPOLYGON
18 Levels: GEOMETRY POINT LINESTRING POLYGON MULTIPOINT MULTILINESTRING MULTIPOLYGON GEOMETRYCOLLECTION ... TRIANGLE
st_bbox(surrey_lsoa)             #obtains the objects value as specific units 
    xmin     ymin     xmax     ymax 
485406.9 154122.5 501181.2 166842.9 
#The spatial extent of a shapefile or R spatial object represents the geographic “edge” or location that is the #furthest north, south east and west. Thus is represents the overall geographic coverage of the spatial object.

Now lets map the new data!

#map the data
ggplot() + 
  annotation_map_tile() + 
  geom_sf(data = surrey_lsoa, aes(fill = count), alpha = 0.5) + 
  scale_fill_gradient2(name ="Number of crimes")

  |                                                                                                                             
  |                                                                                                                       |   0%
  |                                                                                                                             
  |==============================                                                                                         |  25%
  |                                                                                                                             
  |============================================================                                                           |  50%
  |                                                                                                                             
  |=========================================================================================                              |  75%
  |                                                                                                                             
  |=======================================================================================================================| 100%

Plotting via the ‘tmap’ package

The tmap package allows you tp create thematic maps, the syntax is very similar to the ggplot2. Each map can be plot as an image or as an interactive map via the tmap_mode(“view” / “plot” ) function.

tmap_mode("view")
tmap mode set to interactive viewing
tm_shape(surrey_lsoa) + 
  tm_fill("count") + 
  tm_borders("green", lwd = 0.7, alpha = 0.5)
  #tm_text("name", size = "AREA", col = "black")
  #tmap_style("col_blind")

Classification methods

How can we better visualise counts? Count data does not equally represent the population distribution at hand, tmaps allows you to alter the characteristics of theamatic maps via the ‘styles’ function. The different styles result in different binning techniques. Now, when mapping quantitiatve data such as crime counts, typicaly the variables needed to be put into to ‘bins’. As seen in the previous example, the default binning applied to highlight the LSOAs grouped started from 1-10, 11-20, 21-20, 31-40, 41-50 and 51-60 crimes.

These bins were decided on automatically, however we can define more accurate classes that best refelct the distributional character of the data set.

In this example I’ve used the “kmeans”, “jenks” and “sd”.

  • k-means is a method of vector quantisation, originally from signal processing, that aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean, serving as a prototype of the cluster.
  • Jenks (also known as natural breaks, or goodness of fit variance) classifcaiton aims to arrange a set of values into natural classes, that is the most optimal class range found naturally. This method minimizes the variation within each range, so the areas within each range are as close as possible in value to each other.
  • sd classification is a standarised measure of observations deviated from the mean. By showing which values are above or below the mean, this method helps to show which locations are above or below an average mean.

a <- tm_shape(surrey_lsoa) + 
  tm_fill("count", style = "kmeans") + 
  tm_borders(alpha = 0.3)

b <- tm_shape(surrey_lsoa) + 
  tm_fill("count", style = "jenks") + 
  tm_borders(alpha = 0.3)

c <- tm_shape(surrey_lsoa) + 
  tm_fill("count", style = "sd") + 
  tm_borders(alpha = 0.3)


## tmap_arrange

tmap_mode("plot")
tmap mode set to plotting
tmap_arrange(a, b, c)

NA
NA

Using categorical variables (tm_facets)

Just like the tmap_arrange function, tmap_facets are way to produce side-by-side maps (known as small multiples). It is similar to the ‘facet_grid’ function in ggplot2

Following the rdocumentation [https://www.rdocumentation.org/packages/tmap/versions/3.3-2/topics/tm_facets] “Small multiples can be created in two ways: 1) by specifying the by argument with one or two variable names, by which the data is grouped, 2) by specifying multiple variable names in any of the aesthetic argument of the layer functions (for instance, the argument col in tm_fill).”

Typically tm_facets are defined by a categorical variables. For example in this example, I am using tm_facets() to seperate the map into multiple components by lsoa (This isnt’t the best example of a categorical variable, but something like the urban or rural landscape, or deprivation decile, would be more of interest in criminology). You could for example download the 2011 rural/urban classification from open geography portal and join this to our ‘surrey_lsoa’ sf object (using left_join).

If you are interested in doing so the dataset can be found here; [https://www.ons.gov.uk/methodology/geography/geographicalproducts/ruralurbanclassifications/2011ruralurbanclassification]

tm_shape(surrey_lsoa) +
  tm_fill("count",
          style = "quantile",
          palette = "Blues",
          thres.poly = 0) + 
  tm_facets(by="name", 
            free.coords=TRUE, 
            drop.shapes=TRUE) +
  tm_layout(legend.show = FALSE,
            title.position = c("center", "center"), 
            title.size = 20) +
  tm_borders(alpha = 0.5)

Map Layouts - additional features of tmap (optional task)

map style

tm_shape(surrey_lsoa) + 
  tm_fill("count", style = "sd") + 
  tm_borders(alpha = 0.3) + 
  tmap_style("col_blind")

map legends

tm_shape(surrey_lsoa)+
  tm_fill("count", 
          style = "quantile", 
          palette = "Blues", 
          legend.hist = TRUE, 
          legend.is.portrait = TRUE,
          legend.hist.z = 0.1) +
  tm_layout(legend.height = 0.45, 
            legend.width = 0.35,
            legend.outside = FALSE,
            legend.position = c("right", "bottom"),
            frame = FALSE) +
  tm_borders(alpha = 0.5)

compass, scale bar and grid

tm_shape(surrey_lsoa)+
  tm_fill("count", 
          style = "quantile", 
          palette = "Blues", 
          legend.hist = TRUE, 
          legend.is.portrait = TRUE,
          legend.hist.z = 0.1) +
  tm_layout(legend.height = 0.45, 
            legend.width = 0.35,
            legend.outside = FALSE,
            legend.position = c("right", "bottom"),
            frame = FALSE) +
  tm_borders(alpha = 0.5) +
  tm_compass(type="8star", size = 2) +   #compass 
  tm_scale_bar(width = 0.15) +           #scale bar 
  tm_grid()                              #grid

Activity 2

  1. Explore some of the different classification methods such as “bclust” and “hclust” - what are the main differences? To get help on the different methods available use ??tmap-package or search in the help tab

  2. Assign your new bclust and hclust classification maps into separate objects (call them “h” and “b” and plot them together using tmap_arrange()

  3. Plot an interactive map using the “bclust” classification method by changing the command in the tmap_mode() function


#1)

h <- tm_shape(surrey_lsoa) + 
  tm_fill("count", style = "hclust") + 
  tm_borders(alpha = 0.3)


b <- tm_shape(surrey_lsoa) + 
  tm_fill("count", style = "bclust") + 
  tm_borders(alpha = 0.3)


#2) 

tmap_arrange(h,b)
Committee Member: 1(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)
Error in bclust(x = var, centers = n, ...) : 
  Could not find valid cluster solution in 20 replications
Committee Member: 1(1) 2(1) 3(1) 4(1) 5(1) 6(1) 7(1) 8(1) 9(1) 10(1)
Computing Hierarchical Clustering
Committee Member: 1(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)
Error in bclust(x = var, centers = n, ...) : 
  Could not find valid cluster solution in 20 replications
Committee Member: 1(1) 2(1) 3(1) 4(1) 5(1) 6(1) 7(1) 8(1) 9(1) 10(1)
Computing Hierarchical Clustering

#3) 

tmap_mode("view")
tmap mode set to interactive viewing
tm_shape(surrey_lsoa) + 
  tm_fill("count", style = "bclust") + 
  tm_borders(alpha = 0.3)
Committee Member: 1(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)
Error in bclust(x = var, centers = n, ...) : 
  Could not find valid cluster solution in 20 replications
Committee Member: 1(1) 2(1) 3(1) 4(1) 5(1) 6(1) 7(1) 8(1) 9(1) 10(1)
Computing Hierarchical Clustering
LS0tCnRpdGxlOiAiTWFwcGluZyBDcmltZSBEYXRhIGluIFIgLSBTaGFwZWZpbGVzIgphdXRob3I6ICJOYWRpYSBLZW5uYXIsIFJlc2VhcmNoIEFzc29jaWF0ZSB3aXRoIHRoZSBVS0RTIgpkYXRlOiAiOHRoIE1hcmNoIDIwMjMiCm91dHB1dDoKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAp0b2M6IHllcwplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0IHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFKQpgYGAKCiMjIEludHJvIAoKSW4gdGhpcyBzZWN0aW9uIHdlIHdpbGwgYmUgd29ya2luZyB3aXRoIHNoYXBlZmlsZXMuIE1vcmUgc3BlY2lmaWNhbGx5IGhvdyB0byByZWFkIGluIGEgc2hhcGVmaWxlIGFuZCBqb2luIHRoaXMgdG8gb3VyIGFnZ3JlZ2F0ZWQgY3JpbWUgY291bnQgZGF0YSBmcmFtZS4gRnJvbSB0aGVyZSB3ZSBpbnRyb2R1Y2UgY2xhc3NpZmljYXRpb24gbWV0aG9kcyBhcyBhIHdheSB0byBiZXR0ZXIgdmlzdWFsaXplIGNyaW1lIGNvdW50cy4gCgojIyBMb2FkIHBhY2thZ2VzCgpBcyBhbHdheXMgdGhlIGZpcnN0IHN0ZXAgaXMgdG8gbG9hZCB0aGUgbmVjZXNzYXJ5IFIgcGFja2FnZXMgdmlhIHRoZSBsaWJyYXJ5IGZ1bmN0aW9uLiBJZiB5b3UgZG8gbm90IGhhdmUgdGhlc2UgcGFja2FnZXMgaW5zdGFsbGVkIHRoZW4gcGxlYXNlIGZvbGxvdyB0aGUgaW5zdHJ1Y3Rpb25zIGluIHRoZSAqUHJlbGltaW5hcnkgVGFzay5SbWQqIGZpbGUuIAoKYGBge3Igc2V0dXB9CgojIGZvciBkYXRhIHJlYWRpbmcvbWFuaXB1bGF0aW9uIApsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShqYW5pdG9yKQojIGZvciBzcGF0aWFsIGRhdGEgYW5kIGdpcwpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGdnbWFwKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dzcGF0aWFsKQpsaWJyYXJ5KHNwZGVwKQpsaWJyYXJ5KGxlYWZsZXQpIApsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeSh0bWFwKQoKYGBgCgoKIyMgV2hhdCBpcyBhIFNoYXBlZmlsZT8gCgpUaGV5IHJlcHJlc2VudCBhIGdlb3NwYXRpYWwgdmVjdG9yIHRoYXQgaXMgdXNlZCBmb3IgR0lTIHNvZnR3YXJlLiBTaGFwZWZpbGVzIHN0b3JlIGJvdGggZ2VvZ3JwYWhpYyBsb2NhdGlvbiBhbmQgaXRzIGFzc29jaWF0ZWQgYXR0cmlidXRlIGluZm9ybWF0aW9uIAoKVGhlIFNoYXBlZmlsZSBmb3JtYXQgc3RvcmVzIHRoZSBkYXRhIGFzIHByaW1pdGl2ZSBnZW9tZXRyaWMgc2hhcGVzIGxpa2UgcG9pbnRzLCBsaW5lcywgYW5kIHBvbHlnb25zLiBUaGVzZSBzaGFwZXMsIHRvZ2V0aGVyIHdpdGggZGF0YSBhdHRyaWJ1dGVzIHRoYXQgYXJlIGxpbmtlZCB0byBlYWNoIHNoYXBlLCBjcmVhdGUgdGhlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBnZW9ncmFwaGljIGRhdGEuCgpUaGV5IGNvbnRhaW4gZm91ciBtYW5kYXRvcnkgZmlsZSBleHRlbnNpb25zICguc2h4LCAuc2hwLCAuZGJmIGFuZCB0aGUgLnByaikuIAotIFRoZSAuc2hwIGNvbnRhaW5zIHRoZSBnZW9tZXRyeSBkYXRhIChhIDJEIGF4aXMgb3JkZXJpbmcgb2YgY29vcmRpbmF0ZSBkYXRhKQotIFRoZSAuc2h4IGNvbnRhaW5zIHRoZSBwb3NpdGlvbmFsIGluZGV4IG9mIHRoZSBmZWF0dXJlIGdlb21ldHJ5IAotIFRoZSAuZGJmIGNvbnRpbnMgdGhlIGF0dHJpYnV0ZXMgZm9yIGVhY2ggc2hhcGUKLSBUaGUgLnByaiBjb250YWlucyB0aGUgY3MgYW5kIHByb2plY3Rpb24gaW5mb3JtYXRpb24KCkluIGNyaW1pbm9sb2dpY2FsIHJlc2VhcmNoLCB0aGUgTFNPQSBpcyBxdWl0ZSBmcmVxdWVudGx5IHVzZWQgYXMgdGhlIG1haW4gY2Vuc3VzIGdlb2dyYXBoeSAKCgojIyBXaGVyZSB0byBvYnRhaW4gc2hhcGVmaWxlcyAKCkkgY29sbGVjdGVkIG15IHNoYXBlZmlsZSBkYXRhIHZpYSB0aGUgVUtEUyBDZW5zdXMgU3VwcG9ydCAqKGh0dHBzOi8vYm9yZGVycy51a2RhdGFzZXJ2aWNlLmFjLnVrL2Jkcy5odG1sKSouIElmIHlvdSB3YW50IHNwZWNpZmljIGluZm9ybWF0aW9uIGFib3V0IGhvdyB0byB1c2UgdGhlIHdlYnNpdGUsIHBsZWFzZSByZWZlciB0byB0aGUgKkRvd25sb2FkaW5nIHRoZSBkYXRhKiBkb2N1bWVudCBhZ2FpbiAKCgojIyBSZWFkIGluIHRoZSBTaGFwZWZpbGUgZm9yICdTdXJyZXkgSGVhdGgnIAoKYGBge3J9CnNocF9maWxlIDwtIHNmOjpzdF9yZWFkKCIvVXNlcnMvc2hpaGFpdGFvL0xpYnJhcnkvTW9iaWxlIERvY3VtZW50cy9jb21+YXBwbGV+Q2xvdWREb2NzL0RvY3VtZW50cy9NMV9Eb2N1bWVudHMv54ix5LiB5aCh5aSn5a2m5Z+56K6tL01hcHBpbmdDcmltZURhdGEvTWFwcGluZ0NyaW1lRGF0YS9EYXRhL1NoYXBlZmlsZS9lbmdsYW5kX2xzb2FfMjAyMS5zaHAiKQpgYGAKCllvdSBjYW4gYWxzbyB1c2UgdGhlIGhlYWQoKSBmdW5jdGlvbiBpbiBzaGFwZWZpbGVzIQoKYGBge3J9CmhlYWQoc2hwX2ZpbGUpCiNvciB1c2UgJ1ZpZXcoc2hwX2ZpbGUpIHRvIHZpZXcgdGhlIGZ1bGwgZGF0YXNldCB3aGljaCB3aWxsIG9wZW4gdXAgaW4gYSBuZXcgcGFuZWwKYGBgCgoqRnVydGhlciBJbmZvcm1hdGlvbiAtIFRvIGNsYXJpZnksIHRoaXMgaXMgYW4gJ2VtcHR5IHNoYXBlZmlsZScsIGl0IHNpbXBseSBjb250YWlucyB0aGUgYm91bmRhcnkgcHJvZmlsZSBvZiBTdXJyZXkgSGVhdGggYW5kIGRvZXMgbm90IGNvbnRhaW4gYW55IGZ1cnRoZXIgYXR0cmlidXRlIGluZm9ybWF0aW9uLiBIb3dldmVyLCBpZiBpdCBkaWQgY29udGFpbiBmdXJ0aGVyIGF0dHJpYnV0ZSBpbmZvcm1hdGlvbiBzdWNoIGFzIHRoZSBjcmltZSBjb3VudHMsIHBvcHVsYXRpb24gc3RhdGlzdGljcywgSU1EIGNvdW50cywgdGhlbiB5b3Ugd291bGQgbm90IG5lZWQgdG8gam9pbiB0aGUgZGF0YSBhcyB3ZSBkbyBpbiB0aGlzIHdvcmtzaG9wLCBidXQgaW5zdGVhZCB5b3UgY291bGQgbGF5ZXIgdGhlIHNoYXBlZmlsZSBvdmVyIG91ciBzaW1wbGUgZmVhdHVyZSBvYmplY3QgY3JlYXRlZCBpbiBUb3BpYyAxLiBNb3JlIGluZm9ybWF0aW9uIG9uIHRoaXMgbWV0aG9kIGlzIGF2YWlsYWJsZSBmcm9tIHRoZSB3b3Jrc2hvcCB0aGF0IHdhcyBoZWxkIGluIEZlYnJ1YXJ5IC0gYWxsIHJlc291cmNlcyBhcmUgYXZhaWxhYmxlIHZpYSB0aGUgJ0ZlYl8yMDIxJyBmb2xkZXIgZnJvbSB0aGUgZ2l0aHViIGxpbmsgW2h0dHBzOi8vZ2l0aHViLmNvbS9VS0RhdGFTZXJ2aWNlT3Blbi9DcmltZV9EYXRhX2luX1IuZ2l0XSAqCgpMZXRzIHBsb3QgdGhlIGVtcHR5IHNoYXBlZmlsZSBmb3IgU3VycmV5IEhlYXRoIHRvIHNlZSB3aGF0IHdlJ3JlIGFjdHVhbGx5IGxvb2tpbmcgYXQuCgoKYGBge3J9CiMjIFBsb3QgdGhlIFNoYXBlZmlsZSAKZ2dwbG90KCkgKyAKICBnZW9tX3NmKGRhdGEgPSBzaHBfZmlsZSkKYGBgCgoKQXMgeW91IGNhbiBzZWVtLCB3ZSBoYXZlIGEgbWFwIHRoYXQgZGV0YWlscyB0aGUgYm9yZGVycyAoaS5lLiBzaGFwZSkgYmV0d2VlbiBlYWNoIExTT0EgaW4gU3VycmV5LiAKClRoaXMgd29ya3Nob3AgaW5zdGVhZCBqb2lucyB0aGUgJ2NyaW1lJyBkYXRhdHNldCAoaW4gdGliYmxlIGZvcm1hdCkgdG8gdGhlIGFib3ZlIHNoYXBlZmlsZS4gT3VyIG5ld2x5IGNyZWF0ZWQgb2JqZWN0ICdzaHBfZmlsZScgaXMgaW4gZmFjdCBhIHNmIG9iamVjdCwgd2hpY2ggaXMgc2hvcnQgZm9yIGEgJ3NpbXBsZSBmZWF0dXJlIG9iamVjdCcuIFlvdSBjYW4gY2hlY2sgdGhpcyBieSB0eXBpbmcgKmNsYXNzKHNocF9maWxlKSouIAoKCmBgYHtyfQpjbGFzcyhzaHBfZmlsZSkKYGBgCgoKU28gaW4gdG90YWwsIHRoZSBzaGFwZWZpbGUgY29uc2l0cyBvZiA1IHZhcmlhYmxlcy4gVGhlIGZpcnN0IDQgdmFyaWFibGVzIGluZGljYXRlIGluZm9ybWF0aW9uIGFib3V0IHRoYXQgc3BlY2lmaWMgTFNPQSwgd2UgYXJlIGdpdmVuIHRoZSBuYW1lLCBMU09BIGNvZGUgYW5kIExTT0EgbmFtZS4gV2UgY2FuIGlnbm9yZSB0aGUgY29sdW1uICdsYWJlbCcgYXMgdGhpcyBpcyBqdXN0IGFub3RoZXIgcmVmZXJlbmNlIHBvaW50LgoKClRoZSBjb2x1bW4gSSB3YW50IHRvIGRyYXcgYXR0ZW50aW9uIHRvIGlzIHRoZSAnZ2VvbWV0cnkgY29sdW1uJyAKCmBgYHtyfQphdHRyaWJ1dGVzKHNocF9maWxlJGdlb21ldHJ5KQpgYGAKCgpUaGUgZ2VvbWV0cnkgY29sdW1uIGNhbiBiZSBzcGxpdCBpbnRvIHR3byBrZXkgc2VjdGlvbnM7IHRoZSBmZWF0dXJlIGFuZCB0aGUgZ2VvbWV0cnkgCiAgLSBUaGUgZmVhdHVyZSBpbiB0aGlzIGNhc2UgaXMgb3VyIHBvbHlnb24gbGV2ZWwgKHJlZmVyZW5jZWQgYnkgdGhlIG11bHRpcG9seWdvbikgd2hpY2ggaXMgaW4gZmFjdCBhICAqc2ltcGxlIGZlYXR1cmUgZ2VvbWV0ZXJ5IGxpc3QtIGNvbHVtbiAoc2ZjKSogCiAgLSBUaGUgZ2VvbWV0ZXJ5IGFyZSB0aGUgbnVtYmVycyB0aGF0IGZvbGxvdywgYW5kIG1vcmUgdGVjaG5pY2FsbHkga25vd24gYXMgYSAqJ3NpbXBsZSBmZWF0dXJlIGdlb21ldHJ5IChzZmcpKgoKVGhlIGNvbHVtbiBpbiB0aGUgc2YgZGF0YS5mcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBnZW9tZXRyaWVzIGlzIGEgbGlzdCwgb2YgY2xhc3Mgc2ZjLiBXZSBjYW4gcmV0cmlldmUgdGhlIGdlb21ldHJ5IGxpc3QtY29sdW1uIGluIHRoaXMgY2FzZSBieSB1c2luZyBzdF9nZW9tZXRyeS4gCgoKYGBge3IgdG8gdmlldyB0aGUgc2ZjIGFuZCBzZmd9CnN0X2dlb21ldHJ5KHNocF9maWxlKQpgYGAKCgoKTm93IHdlIGhhdmUgYSBiYXNpYyB1bmRlcnN0YW5kaW5nIG9mIHdoYXQgYSBzaGFwZWZpbGUgaXMgYW5kIGhvdyB3ZSBjYW4gaW1wb3J0IHRoZW0gaW50byByLCB0aGUgbmV4dCBzdGVwIGlzIHJ1biBzb21lIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBjcmVhdGUgc29tZSBuZXcgZGF0YWZyYW1lcyB0aGF0IGNhbiB3b3JrIHdpdGggdGhlIGZvcm1hdCBvZiBzaGFwZWZpbGVzLiAKCgoKIyMgR3JvdXAgdGhlIGNyaW1lcyBwZXIgbHNvYSAKClRoZSBvcmlnaW5hbCBjcmltZSBkYXRhIHNldCBjb250YWlucyB0aGUgaW5kaXZpZHVhbCBjb3VudCBvZiByZXBvcnRlZCBjcmltZSB0eXBlcyBhY3Jvc3MgTFNPQVMsIHRoZXJlZm9yZSB0aGUgTFNPQXMgYXJlIHJlcGVhdGVkIG11bHRpcGxlIHRpbWVzLiBUaGlzIGlzIGJlY2F1c2UgeW91IHdvdWxkIGV4cGVjdCB0byBzZWUgbXVsdGlwbGUgY3JpbWUgY291bnRzIGluIG9uZSBMU09BLgoKSW4gb3JkZXIgdG8gaGlnaGxpZ2h0IGhvdyBtYW55IGNyaW1lcyBoYXZlIG9jY3VycmVkIGluIGVhY2ggTFNPQSwgd2UgY2FuIGNvdW50IHRoZSBjcmltZXMgcGVyIExTT0EgYW5kIG9idGFpbmVkIGdyb3VwZWQgc3RhdGlzdGljcy4KCgpgYGB7cn0KY3JpbWVzX2dyb3VwZWRfYnlfbHNvYSA8LSBjcmltZSAlPiUKICBncm91cF9ieShsc29hX2NvZGUpICU+JQogIHN1bW1hcmlzZShjb3VudD1uKCkpCgpoZWFkKGNyaW1lc19ncm91cGVkX2J5X2xzb2EpCgpgYGAKCgoKCgojIyBNZXJnZSB0aGUgc2hhcGVmaWxlIHRvIHRoZSBjcmltZSBkYXRhc2V0CgpJbiBvdXIgbmV3IG9iamVjdCB5b3Ugd2lsbCBzZWUgdHdvIHZhcmlhYmxlcywgdGhlIExTT0EgYW5kIHRoZSBjb3VudCBvZiBjcmltZSBpbiBlYWNoIG9uZS4gIAoKV2UgY2FuIG5vdyBqb2luIHRoZSBTaGFwZWZpbGUgKHRoZSBnZW9zcGF0aWFsIHZlY3RvcikgYW5kIHRoZSBjcmltZXNfZ3JvdXBlZF9ieV9sb3NhICh0aGUgYWdncmVnYXRlZCBkYXRhKQoKVG8gam9pbiB0aGUgY3JpbWVzIHBlciBsc29hIHRvIHRoZSBzaGFwZWZpbGUgd2UgY2FuIHVzZSB0aGUgbGVmdF9qb2luIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbGwgdGhlIHJvd3Mgb2YgdGhlIHRhYmxlIG9uIHRoZSBsZWZ0IHNpZGUgb2YgdGhlIGpvaW4gYW5kIG1hdGNoaW5nIHJvd3MgZm9yIHRoZSB0YWJsZSBvbiB0aGUgcmlnaHQgc2lkZSBvZiBqb2luLgoKCmBgYHtyfQpzdXJyZXlfbHNvYSA8LSBsZWZ0X2pvaW4oc2hwX2ZpbGUsIGNyaW1lc19ncm91cGVkX2J5X2xzb2EsIGJ5ID0gYygibHNvYTIxY2QiID0gImxzb2FfY29kZSIpKQoKaGVhZChzdXJyZXlfbHNvYSkKCnN0X2dlb21ldHJ5X3R5cGUoc3VycmV5X2xzb2EpICAgICN2aWV3IHRoZSBnZW9tZXRlcnkgdHlwZSAKc3RfYmJveChzdXJyZXlfbHNvYSkgICAgICAgICAgICAgI29idGFpbnMgdGhlIG9iamVjdHMgdmFsdWUgYXMgc3BlY2lmaWMgdW5pdHMgCgoKI1RoZSBzcGF0aWFsIGV4dGVudCBvZiBhIHNoYXBlZmlsZSBvciBSIHNwYXRpYWwgb2JqZWN0IHJlcHJlc2VudHMgdGhlIGdlb2dyYXBoaWMg4oCcZWRnZeKAnSBvciBsb2NhdGlvbiB0aGF0IGlzIHRoZSAjZnVydGhlc3Qgbm9ydGgsIHNvdXRoIGVhc3QgYW5kIHdlc3QuIFRodXMgaXMgcmVwcmVzZW50cyB0aGUgb3ZlcmFsbCBnZW9ncmFwaGljIGNvdmVyYWdlIG9mIHRoZSBzcGF0aWFsIG9iamVjdC4KYGBgCgoKTm93IGxldHMgbWFwIHRoZSBuZXcgZGF0YSEKCmBgYHtyfQojbWFwIHRoZSBkYXRhCmdncGxvdCgpICsgCiAgYW5ub3RhdGlvbl9tYXBfdGlsZSgpICsgCiAgZ2VvbV9zZihkYXRhID0gc3VycmV5X2xzb2EsIGFlcyhmaWxsID0gY291bnQpLCBhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50MihuYW1lID0iTnVtYmVyIG9mIGNyaW1lcyIpCgpgYGAKCgoKIyMgUGxvdHRpbmcgdmlhIHRoZSAndG1hcCcgcGFja2FnZQoKVGhlIHRtYXAgcGFja2FnZSBhbGxvd3MgeW91IHRwIGNyZWF0ZSB0aGVtYXRpYyBtYXBzLCB0aGUgc3ludGF4IGlzIHZlcnkgc2ltaWxhciB0byB0aGUgZ2dwbG90Mi4gRWFjaCBtYXAgY2FuIGJlIHBsb3QgYXMgYW4gaW1hZ2Ugb3IgYXMgYW4gaW50ZXJhY3RpdmUgbWFwIHZpYSB0aGUgKnRtYXBfbW9kZSgidmlldyIgLyAicGxvdCIgKSogZnVuY3Rpb24uIAoKCmBgYHtyfQp0bWFwX21vZGUoInZpZXciKQoKdG1fc2hhcGUoc3VycmV5X2xzb2EpICsgCiAgdG1fZmlsbCgiY291bnQiKSArIAogIHRtX2JvcmRlcnMoImdyZWVuIiwgbHdkID0gMC43LCBhbHBoYSA9IDAuNSkKICAjdG1fdGV4dCgibmFtZSIsIHNpemUgPSAiQVJFQSIsIGNvbCA9ICJibGFjayIpCiAgI3RtYXBfc3R5bGUoImNvbF9ibGluZCIpCmBgYAoKCgojIyMgQ2xhc3NpZmljYXRpb24gbWV0aG9kcwoKSG93IGNhbiB3ZSBiZXR0ZXIgdmlzdWFsaXNlIGNvdW50cz8gQ291bnQgZGF0YSBkb2VzIG5vdCBlcXVhbGx5IHJlcHJlc2VudCB0aGUgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24gYXQgaGFuZCwgdG1hcHMgYWxsb3dzIHlvdSB0byBhbHRlciB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZWFtYXRpYyBtYXBzIHZpYSB0aGUgJ3N0eWxlcycgZnVuY3Rpb24uIFRoZSBkaWZmZXJlbnQgc3R5bGVzIHJlc3VsdCBpbiBkaWZmZXJlbnQgYmlubmluZyB0ZWNobmlxdWVzLiAgTm93LCB3aGVuIG1hcHBpbmcgcXVhbnRpdGlhdHZlIGRhdGEgc3VjaCBhcyBjcmltZSBjb3VudHMsIHR5cGljYWx5IHRoZSB2YXJpYWJsZXMgbmVlZGVkIHRvIGJlIHB1dCBpbnRvIHRvICdiaW5zJy4gQXMgc2VlbiBpbiB0aGUgcHJldmlvdXMgZXhhbXBsZSwgdGhlIGRlZmF1bHQgYmlubmluZyBhcHBsaWVkIHRvIGhpZ2hsaWdodCB0aGUgTFNPQXMgZ3JvdXBlZCBzdGFydGVkIGZyb20gMS0xMCwgMTEtMjAsIDIxLTIwLCAzMS00MCwgNDEtNTAgYW5kIDUxLTYwIGNyaW1lcy4KClRoZXNlIGJpbnMgd2VyZSBkZWNpZGVkIG9uIGF1dG9tYXRpY2FsbHksIGhvd2V2ZXIgd2UgY2FuIGRlZmluZSBtb3JlIGFjY3VyYXRlIGNsYXNzZXMgdGhhdCBiZXN0IHJlZmVsY3QgdGhlIGRpc3RyaWJ1dGlvbmFsIGNoYXJhY3RlciBvZiB0aGUgZGF0YSBzZXQuCgoKSW4gdGhpcyBleGFtcGxlIEkndmUgdXNlZCB0aGUgImttZWFucyIsICJqZW5rcyIgYW5kICJzZCIuCgotIGstbWVhbnMgaXMgYSBtZXRob2Qgb2YgdmVjdG9yIHF1YW50aXNhdGlvbiwgb3JpZ2luYWxseSBmcm9tIHNpZ25hbCBwcm9jZXNzaW5nLCB0aGF0IGFpbXMgdG8gcGFydGl0aW9uIG4gb2JzZXJ2YXRpb25zIGludG8gayBjbHVzdGVycyBpbiB3aGljaCBlYWNoIG9ic2VydmF0aW9uIGJlbG9uZ3MgdG8gdGhlIGNsdXN0ZXIgd2l0aCB0aGUgbmVhcmVzdCBtZWFuLCBzZXJ2aW5nIGFzIGEgcHJvdG90eXBlIG9mIHRoZSBjbHVzdGVyLgotIEplbmtzIChhbHNvIGtub3duIGFzIG5hdHVyYWwgYnJlYWtzLCBvciBnb29kbmVzcyBvZiBmaXQgdmFyaWFuY2UpIGNsYXNzaWZjYWl0b24gYWltcyB0byBhcnJhbmdlIGEgc2V0IG9mIHZhbHVlcyBpbnRvIG5hdHVyYWwgY2xhc3NlcywgdGhhdCBpcyB0aGUgbW9zdCBvcHRpbWFsIGNsYXNzIHJhbmdlIGZvdW5kIG5hdHVyYWxseS4gVGhpcyBtZXRob2QgbWluaW1pemVzIHRoZSB2YXJpYXRpb24gd2l0aGluIGVhY2ggcmFuZ2UsIHNvIHRoZSBhcmVhcyB3aXRoaW4gZWFjaCByYW5nZSBhcmUgYXMgY2xvc2UgYXMgcG9zc2libGUgaW4gdmFsdWUgdG8gZWFjaCBvdGhlci4gCi0gc2QgY2xhc3NpZmljYXRpb24gaXMgYSBzdGFuZGFyaXNlZCBtZWFzdXJlIG9mIG9ic2VydmF0aW9ucyBkZXZpYXRlZCBmcm9tIHRoZSBtZWFuLiBCeSBzaG93aW5nIHdoaWNoIHZhbHVlcyBhcmUgYWJvdmUgb3IgYmVsb3cgdGhlIG1lYW4sIHRoaXMgbWV0aG9kIGhlbHBzIHRvIHNob3cgd2hpY2ggbG9jYXRpb25zIGFyZSBhYm92ZSBvciBiZWxvdyBhbiBhdmVyYWdlIG1lYW4uCgoKCmBgYHtyfQoKYSA8LSB0bV9zaGFwZShzdXJyZXlfbHNvYSkgKyAKICB0bV9maWxsKCJjb3VudCIsIHN0eWxlID0gImttZWFucyIpICsgCiAgdG1fYm9yZGVycyhhbHBoYSA9IDAuMykKCmIgPC0gdG1fc2hhcGUoc3VycmV5X2xzb2EpICsgCiAgdG1fZmlsbCgiY291bnQiLCBzdHlsZSA9ICJqZW5rcyIpICsgCiAgdG1fYm9yZGVycyhhbHBoYSA9IDAuMykKCmMgPC0gdG1fc2hhcGUoc3VycmV5X2xzb2EpICsgCiAgdG1fZmlsbCgiY291bnQiLCBzdHlsZSA9ICJzZCIpICsgCiAgdG1fYm9yZGVycyhhbHBoYSA9IDAuMykKCgojIyB0bWFwX2FycmFuZ2UKCnRtYXBfbW9kZSgicGxvdCIpCnRtYXBfYXJyYW5nZShhLCBiLCBjKQoKCmBgYAoKCgoKCiMjIFVzaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyAodG1fZmFjZXRzKQoKSnVzdCBsaWtlIHRoZSB0bWFwX2FycmFuZ2UgZnVuY3Rpb24sIHRtYXBfZmFjZXRzIGFyZSB3YXkgdG8gcHJvZHVjZSBzaWRlLWJ5LXNpZGUgbWFwcyAoa25vd24gYXMgKnNtYWxsIG11bHRpcGxlcyopLiBJdCBpcyBzaW1pbGFyIHRvIHRoZSAnZmFjZXRfZ3JpZCcgZnVuY3Rpb24gaW4gZ2dwbG90MgoKRm9sbG93aW5nIHRoZSByZG9jdW1lbnRhdGlvbiBbaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3RtYXAvdmVyc2lvbnMvMy4zLTIvdG9waWNzL3RtX2ZhY2V0c10gIlNtYWxsIG11bHRpcGxlcyBjYW4gYmUgY3JlYXRlZCBpbiB0d28gd2F5czogMSkgYnkgc3BlY2lmeWluZyB0aGUgYnkgYXJndW1lbnQgd2l0aCBvbmUgb3IgdHdvIHZhcmlhYmxlIG5hbWVzLCBieSB3aGljaCB0aGUgZGF0YSBpcyBncm91cGVkLCAyKSBieSBzcGVjaWZ5aW5nIG11bHRpcGxlIHZhcmlhYmxlIG5hbWVzIGluIGFueSBvZiB0aGUgYWVzdGhldGljIGFyZ3VtZW50IG9mIHRoZSBsYXllciBmdW5jdGlvbnMgKGZvciBpbnN0YW5jZSwgdGhlIGFyZ3VtZW50IGNvbCBpbiB0bV9maWxsKS4iIAoKVHlwaWNhbGx5IHRtX2ZhY2V0cyBhcmUgZGVmaW5lZCBieSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gRm9yIGV4YW1wbGUgaW4gdGhpcyBleGFtcGxlLCBJIGFtIHVzaW5nIHRtX2ZhY2V0cygpIHRvIHNlcGVyYXRlIHRoZSBtYXAgaW50byBtdWx0aXBsZSBjb21wb25lbnRzIGJ5IGxzb2EgKFRoaXMgaXNudCd0IHRoZSBiZXN0IGV4YW1wbGUgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgYnV0IHNvbWV0aGluZyBsaWtlIHRoZSB1cmJhbiBvciBydXJhbCBsYW5kc2NhcGUsIG9yIGRlcHJpdmF0aW9uIGRlY2lsZSwgd291bGQgYmUgbW9yZSBvZiBpbnRlcmVzdCBpbiBjcmltaW5vbG9neSkuIFlvdSBjb3VsZCBmb3IgZXhhbXBsZSBkb3dubG9hZCB0aGUgMjAxMSBydXJhbC91cmJhbiBjbGFzc2lmaWNhdGlvbiBmcm9tIG9wZW4gZ2VvZ3JhcGh5IHBvcnRhbCBhbmQgam9pbiB0aGlzIHRvIG91ciAnc3VycmV5X2xzb2EnIHNmIG9iamVjdCAodXNpbmcgbGVmdF9qb2luKS4gCgpJZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gZG9pbmcgc28gdGhlIGRhdGFzZXQgY2FuIGJlIGZvdW5kIGhlcmU7CltodHRwczovL3d3dy5vbnMuZ292LnVrL21ldGhvZG9sb2d5L2dlb2dyYXBoeS9nZW9ncmFwaGljYWxwcm9kdWN0cy9ydXJhbHVyYmFuY2xhc3NpZmljYXRpb25zLzIwMTFydXJhbHVyYmFuY2xhc3NpZmljYXRpb25dCgoKCmBgYHtyfQp0bV9zaGFwZShzdXJyZXlfbHNvYSkgKwogIHRtX2ZpbGwoImNvdW50IiwKICAgICAgICAgIHN0eWxlID0gInF1YW50aWxlIiwKICAgICAgICAgIHBhbGV0dGUgPSAiQmx1ZXMiLAogICAgICAgICAgdGhyZXMucG9seSA9IDApICsgCiAgdG1fZmFjZXRzKGJ5PSJuYW1lIiwgCiAgICAgICAgICAgIGZyZWUuY29vcmRzPVRSVUUsIAogICAgICAgICAgICBkcm9wLnNoYXBlcz1UUlVFKSArCiAgdG1fbGF5b3V0KGxlZ2VuZC5zaG93ID0gRkFMU0UsCiAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gYygiY2VudGVyIiwgImNlbnRlciIpLCAKICAgICAgICAgICAgdGl0bGUuc2l6ZSA9IDIwKSArCiAgdG1fYm9yZGVycyhhbHBoYSA9IDAuNSkKYGBgCgoKCgoKCiMjIE1hcCBMYXlvdXRzIC0gYWRkaXRpb25hbCBmZWF0dXJlcyBvZiB0bWFwIChvcHRpb25hbCB0YXNrKQoKIyMjIG1hcCBzdHlsZSAKCmBgYHtyfQp0bV9zaGFwZShzdXJyZXlfbHNvYSkgKyAKICB0bV9maWxsKCJjb3VudCIsIHN0eWxlID0gInNkIikgKyAKICB0bV9ib3JkZXJzKGFscGhhID0gMC4zKSArIAogIHRtYXBfc3R5bGUoImNvbF9ibGluZCIpCmBgYAoKIyMjIG1hcCBsZWdlbmRzCgpgYGB7cn0KdG1fc2hhcGUoc3VycmV5X2xzb2EpKwogIHRtX2ZpbGwoImNvdW50IiwgCiAgICAgICAgICBzdHlsZSA9ICJxdWFudGlsZSIsIAogICAgICAgICAgcGFsZXR0ZSA9ICJCbHVlcyIsIAogICAgICAgICAgbGVnZW5kLmhpc3QgPSBUUlVFLCAKICAgICAgICAgIGxlZ2VuZC5pcy5wb3J0cmFpdCA9IFRSVUUsCiAgICAgICAgICBsZWdlbmQuaGlzdC56ID0gMC4xKSArCiAgdG1fbGF5b3V0KGxlZ2VuZC5oZWlnaHQgPSAwLjQ1LCAKICAgICAgICAgICAgbGVnZW5kLndpZHRoID0gMC4zNSwKICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBGQUxTRSwKICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygicmlnaHQiLCAiYm90dG9tIiksCiAgICAgICAgICAgIGZyYW1lID0gRkFMU0UpICsKICB0bV9ib3JkZXJzKGFscGhhID0gMC41KQpgYGAKCiMjIyBjb21wYXNzLCBzY2FsZSBiYXIgYW5kIGdyaWQKCmBgYHtyfQp0bV9zaGFwZShzdXJyZXlfbHNvYSkrCiAgdG1fZmlsbCgiY291bnQiLCAKICAgICAgICAgIHN0eWxlID0gInF1YW50aWxlIiwgCiAgICAgICAgICBwYWxldHRlID0gIkJsdWVzIiwgCiAgICAgICAgICBsZWdlbmQuaGlzdCA9IFRSVUUsIAogICAgICAgICAgbGVnZW5kLmlzLnBvcnRyYWl0ID0gVFJVRSwKICAgICAgICAgIGxlZ2VuZC5oaXN0LnogPSAwLjEpICsKICB0bV9sYXlvdXQobGVnZW5kLmhlaWdodCA9IDAuNDUsIAogICAgICAgICAgICBsZWdlbmQud2lkdGggPSAwLjM1LAogICAgICAgICAgICBsZWdlbmQub3V0c2lkZSA9IEZBTFNFLAogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwKICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSkgKwogIHRtX2JvcmRlcnMoYWxwaGEgPSAwLjUpICsKICB0bV9jb21wYXNzKHR5cGU9IjhzdGFyIiwgc2l6ZSA9IDIpICsgICAjY29tcGFzcyAKICB0bV9zY2FsZV9iYXIod2lkdGggPSAwLjE1KSArICAgICAgICAgICAjc2NhbGUgYmFyIAogIHRtX2dyaWQoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNncmlkCgpgYGAKCgoKCgoKCgoKCgoKIyMgQWN0aXZpdHkgMgoKMS4gRXhwbG9yZSBzb21lIG9mIHRoZSBkaWZmZXJlbnQgY2xhc3NpZmljYXRpb24gbWV0aG9kcyBzdWNoIGFzICJiY2x1c3QiIGFuZCAiaGNsdXN0IiAgLSB3aGF0IGFyZSB0aGUgbWFpbiBkaWZmZXJlbmNlcz8gVG8gZ2V0IGhlbHAgb24gdGhlIGRpZmZlcmVudCBtZXRob2RzIGF2YWlsYWJsZSB1c2UgKj8/dG1hcC1wYWNrYWdlKiBvciBzZWFyY2ggaW4gdGhlIGhlbHAgdGFiIAoKMi4gQXNzaWduIHlvdXIgbmV3IGJjbHVzdCBhbmQgaGNsdXN0IGNsYXNzaWZpY2F0aW9uIG1hcHMgaW50byBzZXBhcmF0ZSBvYmplY3RzIChjYWxsIHRoZW0gImgiIGFuZCAiYiIgYW5kIHBsb3QgdGhlbSB0b2dldGhlciB1c2luZyB0bWFwX2FycmFuZ2UoKQoKCjMuIFBsb3QgYW4gaW50ZXJhY3RpdmUgbWFwIHVzaW5nIHRoZSAiYmNsdXN0IiBjbGFzc2lmaWNhdGlvbiBtZXRob2QgYnkgY2hhbmdpbmcgdGhlIGNvbW1hbmQgaW4gdGhlIHRtYXBfbW9kZSgpIGZ1bmN0aW9uIAoKICAgCmBgYHtyIHR5cGUgeW91ciBhbnN3ZXIgaGVyZX0KCiMxKQoKaCA8LSB0bV9zaGFwZShzdXJyZXlfbHNvYSkgKyAKICB0bV9maWxsKCJjb3VudCIsIHN0eWxlID0gImhjbHVzdCIpICsgCiAgdG1fYm9yZGVycyhhbHBoYSA9IDAuMykKCgpiIDwtIHRtX3NoYXBlKHN1cnJleV9sc29hKSArIAogIHRtX2ZpbGwoImNvdW50Iiwgc3R5bGUgPSAiYmNsdXN0IikgKyAKICB0bV9ib3JkZXJzKGFscGhhID0gMC4zKQoKCiMyKSAKCnRtYXBfYXJyYW5nZShoLGIpCgoKCiMzKSAKCnRtYXBfbW9kZSgidmlldyIpCgp0bV9zaGFwZShzdXJyZXlfbHNvYSkgKyAKICB0bV9maWxsKCJjb3VudCIsIHN0eWxlID0gImJjbHVzdCIpICsgCiAgdG1fYm9yZGVycyhhbHBoYSA9IDAuMykKCgoKYGBgCiAKCgoKCgoKCgoKCgoKCgoKCgo=